#include "UIComm.h"
#include "GdiHepler.h"

namespace ui {
    namespace GdiHelper {

        Gdiplus::Bitmap *CreateBitmapFromHBITMAP(HBITMAP hBitmap) {
            BITMAP bmp = {0};
            if (0 == GetObject(hBitmap, sizeof(BITMAP), (LPVOID) &bmp)) {
                return FALSE;
            }
            // Although we can get bitmap data address by bmp.bmBits member of BITMAP
            // which is got by GetObject function sometime,
            // we can determine the bitmap data in the HBITMAP is arranged bottom-up
            // or top-down, so we should always use GetDIBits to get bitmap data.
            BYTE *piexlsSrc = NULL;
            LONG cbSize = bmp.bmWidthBytes * bmp.bmHeight;
            piexlsSrc = new BYTE[cbSize];
            BITMAPINFO bmpInfo = {0};
            // We should initialize the first six members of BITMAPINFOHEADER structure.
            // A bottom-up DIB is specified by setting the height to a positive number,
            // while a top-down DIB is specified by setting the height to a negative number.
            bmpInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
            bmpInfo.bmiHeader.biWidth = bmp.bmWidth;
            bmpInfo.bmiHeader.biHeight = bmp.bmHeight;  // 正数，说明数据从下到上，如未负数，则从上到下
            bmpInfo.bmiHeader.biPlanes = bmp.bmPlanes;
            bmpInfo.bmiHeader.biBitCount = bmp.bmBitsPixel;
            bmpInfo.bmiHeader.biCompression = BI_RGB;
            HDC hdcScreen = CreateDC(L"DISPLAY", NULL, NULL, NULL);
            LONG cbCopied = GetDIBits(hdcScreen, hBitmap, 0, bmp.bmHeight,
                                      piexlsSrc, &bmpInfo, DIB_RGB_COLORS);
            DeleteDC(hdcScreen);
            if (0 == cbCopied) {
                delete[]  piexlsSrc;
                return FALSE;
            }
            // Create an GDI+ Bitmap has the same dimensions with hbitmap
            Gdiplus::Bitmap *pBitmap = new Gdiplus::Bitmap(bmp.bmWidth, bmp.bmHeight, PixelFormat32bppPARGB);
            // Access to the Gdiplus::Bitmap's pixel data
            Gdiplus::BitmapData bitmapData;
            Gdiplus::Rect rect(0, 0, bmp.bmWidth, bmp.bmHeight);
            if (Gdiplus::Ok != pBitmap->LockBits(&rect, Gdiplus::ImageLockModeRead,
                                                 PixelFormat32bppPARGB, &bitmapData)) {
                delete (pBitmap);
                return NULL;
            }
            BYTE *pixelsDest = (BYTE *) bitmapData.Scan0;
            int nLinesize = bmp.bmWidth * sizeof(UINT);
            int nHeight = bmp.bmHeight;
            // Copy pixel data from HBITMAP by bottom-up.
            for (int y = 0; y < nHeight; y++) {
                // 从下到上复制数据，因为前面设置高度时是正数。
                memcpy_s(
                        (pixelsDest + y * nLinesize),
                        nLinesize,
                        (piexlsSrc + (nHeight - y - 1) * nLinesize),
                        nLinesize);
            }
            // Copy the data in temporary buffer to pBitmap
            if (Gdiplus::Ok != pBitmap->UnlockBits(&bitmapData)) {
                delete pBitmap;
            }
            delete[]  piexlsSrc;
            return pBitmap;
        }

    }

}

namespace Gdiplus {
    #ifdef DEFINE_GUID
    #undef DEFINE_GUID
    #define DEFINE_GUID(name,l,w1,w2,b1,b2,b3,b4,b5,b6,b7,b8) EXTERN_C const GUID DECLSPEC_SELECTANY name = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } }
    #endif
//---------------------------------------------------------------------------
// Image file format identifiers
//---------------------------------------------------------------------------

    DEFINE_GUID(ImageFormatUndefined, 0xb96b3ca9,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatMemoryBMP, 0xb96b3caa,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatBMP, 0xb96b3cab,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatEMF, 0xb96b3cac,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatWMF, 0xb96b3cad,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatJPEG, 0xb96b3cae,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatPNG, 0xb96b3caf,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatGIF, 0xb96b3cb0,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatTIFF, 0xb96b3cb1,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatEXIF, 0xb96b3cb2,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);
    DEFINE_GUID(ImageFormatIcon, 0xb96b3cb5,0x0728,0x11d3,0x9d,0x7b,0x00,0x00,0xf8,0x1e,0xf3,0x2e);

//---------------------------------------------------------------------------
// Predefined multi-frame dimension IDs
//---------------------------------------------------------------------------

    DEFINE_GUID(FrameDimensionTime, 0x6aedbd6d,0x3fb5,0x418a,0x83,0xa6,0x7f,0x45,0x22,0x9d,0xc8,0x72);
    DEFINE_GUID(FrameDimensionResolution, 0x84236f7b,0x3bd3,0x428f,0x8d,0xab,0x4e,0xa1,0x43,0x9c,0xa3,0x15);
    DEFINE_GUID(FrameDimensionPage, 0x7462dc86,0x6180,0x4c7e,0x8e,0x3f,0xee,0x73,0x33,0xa7,0xa4,0x83);

//---------------------------------------------------------------------------
// Property sets
//---------------------------------------------------------------------------

    DEFINE_GUID(FormatIDImageInformation, 0xe5836cbe,0x5eef,0x4f1d,0xac,0xde,0xae,0x4c,0x43,0xb6,0x08,0xce);
    DEFINE_GUID(FormatIDJpegAppHeaders, 0x1c4afdcd,0x6177,0x43cf,0xab,0xc7,0x5f,0x51,0xaf,0x39,0xee,0x85);

//---------------------------------------------------------------------------
// Encoder parameter sets
//---------------------------------------------------------------------------

    DEFINE_GUID(EncoderCompression, 0xe09d739d,0xccd4,0x44ee,0x8e,0xba,0x3f,0xbf,0x8b,0xe4,0xfc,0x58);
    DEFINE_GUID(EncoderColorDepth, 0x66087055,0xad66,0x4c7c,0x9a,0x18,0x38,0xa2,0x31,0x0b,0x83,0x37);
    DEFINE_GUID(EncoderScanMethod, 0x3a4e2661,0x3109,0x4e56,0x85,0x36,0x42,0xc1,0x56,0xe7,0xdc,0xfa);
    DEFINE_GUID(EncoderVersion, 0x24d18c76,0x814a,0x41a4,0xbf,0x53,0x1c,0x21,0x9c,0xcc,0xf7,0x97);
    DEFINE_GUID(EncoderRenderMethod, 0x6d42c53a,0x229a,0x4825,0x8b,0xb7,0x5c,0x99,0xe2,0xb9,0xa8,0xb8);
    DEFINE_GUID(EncoderQuality, 0x1d5be4b5,0xfa4a,0x452d,0x9c,0xdd,0x5d,0xb3,0x51,0x05,0xe7,0xeb);
    DEFINE_GUID(EncoderTransformation,0x8d0eb2d1,0xa58e,0x4ea8,0xaa,0x14,0x10,0x80,0x74,0xb7,0xb6,0xf9);
    DEFINE_GUID(EncoderLuminanceTable,0xedb33bce,0x0266,0x4a77,0xb9,0x04,0x27,0x21,0x60,0x99,0xe7,0x17);
    DEFINE_GUID(EncoderChrominanceTable,0xf2e455dc,0x09b3,0x4316,0x82,0x60,0x67,0x6a,0xda,0x32,0x48,0x1c);
    DEFINE_GUID(EncoderSaveFlag,0x292266fc,0xac40,0x47bf,0x8c, 0xfc, 0xa8, 0x5b, 0x89, 0xa6, 0x55, 0xde);

#if (GDIPVER >= 0x0110)
    DEFINE_GUID(EncoderColorSpace,0xae7a62a0,0xee2c,0x49d8,0x9d,0x7,0x1b,0xa8,0xa9,0x27,0x59,0x6e);
    DEFINE_GUID(EncoderImageItems,0x63875e13,0x1f1d,0x45ab,0x91, 0x95, 0xa2, 0x9b, 0x60, 0x66, 0xa6, 0x50);
    DEFINE_GUID(EncoderSaveAsCMYK,0xa219bbc9, 0xa9d, 0x4005, 0xa3, 0xee, 0x3a, 0x42, 0x1b, 0x8b, 0xb0, 0x6c);
#endif //(GDIPVER >= 0x0110)

    DEFINE_GUID(CodecIImageBytes,0x025d1823,0x6c7d,0x447b,0xbb, 0xdb, 0xa3, 0xcb, 0xc3, 0xdf, 0xa2, 0xfc);


//-----------------------------------------------------------------------------
// GDI+ effect GUIDs
//-----------------------------------------------------------------------------

// {633C80A4-1843-482b-9EF2-BE2834C5FDD4}
    const GUID BlurEffectGuid =
            { 0x633c80a4, 0x1843, 0x482b, { 0x9e, 0xf2, 0xbe, 0x28, 0x34, 0xc5, 0xfd, 0xd4 } };

// {63CBF3EE-C526-402c-8F71-62C540BF5142}
    const GUID SharpenEffectGuid =
            { 0x63cbf3ee, 0xc526, 0x402c, { 0x8f, 0x71, 0x62, 0xc5, 0x40, 0xbf, 0x51, 0x42 } };

// {718F2615-7933-40e3-A511-5F68FE14DD74}
    const GUID ColorMatrixEffectGuid =
            { 0x718f2615, 0x7933, 0x40e3, { 0xa5, 0x11, 0x5f, 0x68, 0xfe, 0x14, 0xdd, 0x74 } };

// {A7CE72A9-0F7F-40d7-B3CC-D0C02D5C3212}
    const GUID ColorLUTEffectGuid =
            { 0xa7ce72a9, 0xf7f, 0x40d7, { 0xb3, 0xcc, 0xd0, 0xc0, 0x2d, 0x5c, 0x32, 0x12 } };

// {D3A1DBE1-8EC4-4c17-9F4C-EA97AD1C343D}
    const GUID BrightnessContrastEffectGuid =
            { 0xd3a1dbe1, 0x8ec4, 0x4c17, { 0x9f, 0x4c, 0xea, 0x97, 0xad, 0x1c, 0x34, 0x3d } };

// {8B2DD6C3-EB07-4d87-A5F0-7108E26A9C5F}
    const GUID HueSaturationLightnessEffectGuid =
            { 0x8b2dd6c3, 0xeb07, 0x4d87, { 0xa5, 0xf0, 0x71, 0x8, 0xe2, 0x6a, 0x9c, 0x5f } };

// {99C354EC-2A31-4f3a-8C34-17A803B33A25}
    const GUID LevelsEffectGuid =
            { 0x99c354ec, 0x2a31, 0x4f3a, { 0x8c, 0x34, 0x17, 0xa8, 0x3, 0xb3, 0x3a, 0x25 } };

// {1077AF00-2848-4441-9489-44AD4C2D7A2C}
    const GUID TintEffectGuid =
            { 0x1077af00, 0x2848, 0x4441, { 0x94, 0x89, 0x44, 0xad, 0x4c, 0x2d, 0x7a, 0x2c } };

// {537E597D-251E-48da-9664-29CA496B70F8}
    const GUID ColorBalanceEffectGuid =
            { 0x537e597d, 0x251e, 0x48da, { 0x96, 0x64, 0x29, 0xca, 0x49, 0x6b, 0x70, 0xf8 } };

// {74D29D05-69A4-4266-9549-3CC52836B632}
    const GUID RedEyeCorrectionEffectGuid =
            { 0x74d29d05, 0x69a4, 0x4266, { 0x95, 0x49, 0x3c, 0xc5, 0x28, 0x36, 0xb6, 0x32 } };

// {DD6A0022-58E4-4a67-9D9B-D48EB881A53D}
    const GUID ColorCurveEffectGuid =
            { 0xdd6a0022, 0x58e4, 0x4a67, { 0x9d, 0x9b, 0xd4, 0x8e, 0xb8, 0x81, 0xa5, 0x3d }
            };

//-----------------------------------------------------------------------------

    Status Bitmap::ApplyEffect(
            IN  Bitmap **inputs,
            IN  INT numInputs,
            IN  Effect *effect,
            IN  RECT *ROI,                     // optional parameter.
            OUT RECT *outputRect,              // optional parameter.
            OUT Bitmap **output
    ) {
        if (numInputs < 0) {
            return InvalidParameter;
        }

        GpBitmap *outputNative = NULL;
        GpBitmap **nativeInputs = new GpBitmap *[numInputs];

        if (NULL == nativeInputs) {
            return OutOfMemory;
        }

        for (int i = 0; i < numInputs; i++) {
            nativeInputs[i] = static_cast<GpBitmap *>(inputs[i]->nativeImage);
        }

        if (effect->auxData) {
            DllExports::GdipFree(effect->auxData);
            effect->auxData = NULL;
            effect->auxDataSize = 0;
        }

        Status status = DllExports::GdipBitmapCreateApplyEffect(
                nativeInputs,
                numInputs,
                effect->nativeEffect,
                ROI,
                outputRect,
                &outputNative,
                effect->useAuxData,
                &effect->auxData,
                &effect->auxDataSize
        );

        if ((Ok == status) && outputNative) {
            *output = new Bitmap(outputNative, status);

            if (NULL == *output) {
                status = OutOfMemory;
                DllExports::GdipDisposeImage(outputNative);
            }
        } else {
            *output = NULL;
        }

        delete[] nativeInputs;

        return status;
    }


    Status Bitmap::ApplyEffect(Effect *effect, RECT *ROI) {
        if (effect->auxData) {
            DllExports::GdipFree(effect->auxData);
            effect->auxData = NULL;
            effect->auxDataSize = 0;
        }

        return DllExports::GdipBitmapApplyEffect(
                static_cast<GpBitmap *>(nativeImage),
                effect->nativeEffect,
                ROI,
                effect->useAuxData,
                &effect->auxData,
                &effect->auxDataSize
        );
    }
}